package cn.chinasyq.photoquan.view;

import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentStatePagerAdapter;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.animation.Interpolator;

import java.lang.reflect.Field;

/**
 * Auto Scroll View Pager
 * <ul>
 * <strong>Basic Setting and Usage</strong>
 * <li>{@link #startAutoScroll()} start auto scroll, or {@link # startAutoScroll(int)} start auto scroll delayed</li>
 * <li>{@link #stopAutoScroll()} stop auto scroll</li>
 * <li>{@link #setInterval(long)} set auto scroll time in milliseconds, default is {@link #DEFAULT_INTERVAL}</li>
 * </ul>
 * <ul>
 * <strong>Advanced Settings and Usage</strong>
 * <li>{@link #setDirection(int)} set auto scroll direction</li>
 * <li>{@link # setCycle(boolean)} set whether automatic cycle when auto scroll reaching the last or first item, default
 * is true</li>
 * <li>{@link #setSlideBorderMode(int)} set how to process when sliding at the last or first item</li>
 * <li>{@link #setStopScrollWhenTouch(boolean)} set whether stop auto scroll when touching, default is true</li>
 * </ul>
 * 
 * @author <a href="http://www.trinea.cn" target="_blank">Trinea</a> 2013-12-30
 */
public class AutoScrollViewPager extends ViewPager {

    public static final int        DEFAULT_INTERVAL            = 3000;

    public static final int        LEFT                        = 0;
    public static final int        RIGHT                       = 1;

    /** do nothing when sliding at the last or first item **/
    public static final int        SLIDE_BORDER_MODE_NONE      = 0;
    /** cycle when sliding at the last or first item **/
    public static final int        SLIDE_BORDER_MODE_CYCLE     = 1;
    /** deliver event to parent when sliding at the last or first item **/
    public static final int        SLIDE_BORDER_MODE_TO_PARENT = 2;

    /** auto scroll time in milliseconds, default is {@link #DEFAULT_INTERVAL} **/
    private long                   interval                    = DEFAULT_INTERVAL;
    /** auto scroll direction, default is {@link #RIGHT} **/
    private int                    direction                   = RIGHT;
    /** whether stop auto scroll when touching, default is true **/
    private boolean                stopScrollWhenTouch         = true;
    /** how to process when sliding at the last or first item, default is {@link #SLIDE_BORDER_MODE_NONE} **/
    private int                    slideBorderMode             = SLIDE_BORDER_MODE_NONE;
    /** whether animating when auto scroll at the last or first item **/
    private boolean                isBorderAnimation           = true;

    private Handler                handler;
    private boolean                isAutoScroll                = true;
    private boolean                isStopByTouch               = false;
    private CustomDurationScroller scroller                    = null;

    public static final int        SCROLL_WHAT                 = 0;
    
    private FragmentStatePagerAdapter adapter;
    private FragmentStatePagerAdapter dataAdapter;
    private FragmentManager fragmentManager;

    public AutoScrollViewPager(Context paramContext){
        super(paramContext);
        init(paramContext);
    }

    public AutoScrollViewPager(Context paramContext, AttributeSet paramAttributeSet){
        super(paramContext, paramAttributeSet);
        init(paramContext);
    }

    private void init(Context paramContext) {
    	fragmentManager = ((FragmentActivity)paramContext).getSupportFragmentManager();
        handler = new MyHandler();
        setViewPagerScroller();
    }

    /**
     * start auto scroll, first scroll delay time is {@link #getInterval()}
     */
    public void startAutoScroll() {
    	startAutoScroll(interval);
    }

    /**
     * start auto scroll
     * 
     * @param delayTimeInMills first scroll delay time
     */
    public void startAutoScroll(long delayTimeInMills) {
    	PagerAdapter adapter = getAdapter();
        if (adapter == null || ( adapter.getCount()) <= 1) {
            return;
        }
        isAutoScroll = true;
        sendScrollMessage(delayTimeInMills);
    }

    /**
     * stop auto scroll
     */
    public void stopAutoScroll() {

        isAutoScroll = false;
        handler.removeMessages(SCROLL_WHAT);
    }

    /**
     * set the factor by which the duration of sliding animation will change
     */
    public void setScrollDurationFactor(double scrollFactor) {
        scroller.setScrollDurationFactor(scrollFactor);
    }

    private void sendScrollMessage(long delayTimeInMills) {
        /** remove messages before, keeps one message is running at most **/
        handler.removeMessages(SCROLL_WHAT);
        handler.sendEmptyMessageDelayed(SCROLL_WHAT, delayTimeInMills);
    }

    /**
     * set ViewPager scroller to change animation duration when sliding
     */
    private void setViewPagerScroller() {
        try {
            Field scrollerField = ViewPager.class.getDeclaredField("mScroller");
            scrollerField.setAccessible(true);
            Field interpolatorField = ViewPager.class.getDeclaredField("sInterpolator");
            interpolatorField.setAccessible(true);

            scroller = new CustomDurationScroller(getContext(), (Interpolator)interpolatorField.get(null));
            scrollerField.set(this, scroller);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * scroll only once
     */
    public void scrollOnce() {
        PagerAdapter adapter = getAdapter();
        int currentItem = getCurrentItem();
        if (adapter == null || ( adapter.getCount()) <= 1) {
            return;
        }
        int nextItem = (direction == LEFT) ? --currentItem : ++currentItem;
        setCurrentItem(nextItem, true);
        
    }

    /**
     * <ul>
     * if stopScrollWhenTouch is true
     * <li>if event is down, stop auto scroll.</li>
     * <li>if event is up, start auto scroll again.</li>
     * </ul>
     */
    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        if (stopScrollWhenTouch) {
            if (ev.getAction() == MotionEvent.ACTION_DOWN && isAutoScroll) {
                isStopByTouch = true;
                stopAutoScroll();
            } else if (ev.getAction() == MotionEvent.ACTION_UP && isStopByTouch) {
            	isStopByTouch = false;
                startAutoScroll();
            }
        }
        getParent().requestDisallowInterceptTouchEvent(true);
        return super.onTouchEvent(ev);
    }


    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        if (stopScrollWhenTouch) {
            if (ev.getAction() == MotionEvent.ACTION_DOWN ) {
                isStopByTouch = true;
                stopAutoScroll();
            } else if (ev.getAction() == MotionEvent.ACTION_UP  || ev.getAction() == MotionEvent.ACTION_CANCEL) {
                isStopByTouch = false;
                startAutoScroll();
            }
        }
        return super.onInterceptTouchEvent(ev);
    }

	private class MyHandler extends Handler {

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);

            switch (msg.what) {
                case SCROLL_WHAT:
                	if (isAutoScroll) {
                		scrollOnce();
                		sendScrollMessage(interval);
					}
                default:
                    break;
            }
        }
    }

    /**
     * get auto scroll time in milliseconds, default is {@link #DEFAULT_INTERVAL}
     * 
     * @return the interval
     */
    public long getInterval() {
        return interval;
    }

    /**
     * set auto scroll time in milliseconds, default is {@link #DEFAULT_INTERVAL}
     * 
     * @param interval the interval to set
     */
    public void setInterval(long interval) {
        this.interval = interval;
    }

    /**
     * get auto scroll direction
     * 
     * @return {@link #LEFT} or {@link #RIGHT}, default is {@link #RIGHT}
     */
    public int getDirection() {
        return (direction == LEFT) ? LEFT : RIGHT;
    }

    /**
     * set auto scroll direction
     * 
     * @param direction {@link #LEFT} or {@link #RIGHT}, default is {@link #RIGHT}
     */
    public void setDirection(int direction) {
        this.direction = direction;
    }

    /**
     * whether stop auto scroll when touching, default is true
     * 
     * @return the stopScrollWhenTouch
     */
    public boolean isStopScrollWhenTouch() {
        return stopScrollWhenTouch;
    }

    /**
     * set whether stop auto scroll when touching, default is true
     * 
     * @param stopScrollWhenTouch
     */
    public void setStopScrollWhenTouch(boolean stopScrollWhenTouch) {
        this.stopScrollWhenTouch = stopScrollWhenTouch;
    }

    /**
     * get how to process when sliding at the last or first item
     * 
     * @return the slideBorderMode {@link #SLIDE_BORDER_MODE_NONE}, {@link #SLIDE_BORDER_MODE_TO_PARENT},
     * {@link #SLIDE_BORDER_MODE_CYCLE}, default is {@link #SLIDE_BORDER_MODE_NONE}
     */
    public int getSlideBorderMode() {
        return slideBorderMode;
    }

    /**
     * set how to process when sliding at the last or first item
     * 
     * @param slideBorderMode {@link #SLIDE_BORDER_MODE_NONE}, {@link #SLIDE_BORDER_MODE_TO_PARENT},
     * {@link #SLIDE_BORDER_MODE_CYCLE}, default is {@link #SLIDE_BORDER_MODE_NONE}
     */
    public void setSlideBorderMode(int slideBorderMode) {
        this.slideBorderMode = slideBorderMode;
    }

    /**
     * whether animating when auto scroll at the last or first item, default is true
     * 
     * @return
     */
    public boolean isBorderAnimation() {
        return isBorderAnimation;
    }

    /**
     * set whether animating when auto scroll at the last or first item, default is true
     * 
     * @param isBorderAnimation
     */
    public void setBorderAnimation(boolean isBorderAnimation) {
        this.isBorderAnimation = isBorderAnimation;
    }
    
    public FragmentStatePagerAdapter getAdapter() {
		return adapter;
	}

	public FragmentStatePagerAdapter getDataAdapter() {
		return dataAdapter;
	}

	public void setDataAdapter(FragmentManager fragmentManager,FragmentStatePagerAdapter fragmentPagerAdapter){
    	adapter = fragmentPagerAdapter == null ? null : new AutoScrollPagerAdapter(fragmentManager,fragmentPagerAdapter);
		super.setAdapter(adapter);

		if (fragmentPagerAdapter != null && fragmentPagerAdapter.getCount() > 0) {
			if (fragmentPagerAdapter.getCount() > 1) {
				setCurrentItem(1, false);
			}else {
				setCurrentItem(0, false);
			}
		}
		this.dataAdapter = fragmentPagerAdapter;
    }
    @Override
	public void setAdapter(PagerAdapter arg0) {
		adapter = arg0 == null ? null : new AutoScrollPagerAdapter(fragmentManager,(FragmentStatePagerAdapter)arg0);
		super.setAdapter(adapter);

		if (arg0 != null && arg0.getCount() != 0) {
			if (arg0.getCount() > 1) {
				setCurrentItem(1, false);
			}else {
				setCurrentItem(0, false);
			}
		}
		this.dataAdapter = (FragmentStatePagerAdapter) arg0;
	}
	// 对setAdapter的数据进行包装
	private class AutoScrollPagerAdapter extends FragmentStatePagerAdapter {
		private final FragmentStatePagerAdapter dataAdapter;

		public AutoScrollPagerAdapter(FragmentManager fm,FragmentStatePagerAdapter dataAdapter) {
			super(fm);
			this.dataAdapter = dataAdapter;
		}

		@Override
		// 关键之一:修改Count长度
		public int getCount() {
			return dataAdapter.getCount() > 1 ? dataAdapter.getCount() + 2 : dataAdapter.getCount();
		}

		@Override
		public Fragment getItem(int position) {
			if (position == 0) {
				return dataAdapter.getItem(dataAdapter.getCount() - 1);
			} else if (position == dataAdapter.getCount() + 1) {
				return dataAdapter.getItem(0);
			} else {
				return dataAdapter.getItem( position - 1);
			}
		}

        @Override
        public void notifyDataSetChanged() {
            Log.e("notifyDataSetChanged","notifyDataSetChanged=========");
//            AutoScrollViewPager.this.removeAllViewsInLayout();
//            AutoScrollViewPager.super.setAdapter(adapter);
            super.notifyDataSetChanged();
        }
    }
	
	// 包装setOnPageChangeListener的数据
		private class MyOnPageChangeListener implements OnPageChangeListener {
			private final OnPageChangeListener listener;
			// 是否已经提前触发了OnPageSelected事件
			private boolean alreadyTriggerOnPageSelected;

			public MyOnPageChangeListener(OnPageChangeListener listener) {
				this.listener = listener;
			}

			@Override
			// 关键之三:
			public void onPageScrollStateChanged(int arg0) {
				if (arg0 == SCROLL_STATE_IDLE) {
					if (getCurrentItem() + 1 == 0) {
						setCurrentItem(dataAdapter.getCount() - 1, false);
					} else if (getCurrentItem() + 1 == dataAdapter.getCount() + 1) {
						setCurrentItem(0, false);
					}
				}

				listener.onPageScrollStateChanged(arg0);
			}

			@Override
			public void onPageScrolled(int arg0, float arg1, int arg2) {
				listener.onPageScrolled(arg0, arg1, arg2);
			}

			@Override
			// 关键四:
			public void onPageSelected(int arg0) {
				if (arg0 == 0) {
					listener.onPageSelected(dataAdapter.getCount() - 1);
					alreadyTriggerOnPageSelected = true;
				} else if (arg0 == dataAdapter.getCount() + 1) {
					listener.onPageSelected(0);
					alreadyTriggerOnPageSelected = true;
				} else {
					if (!alreadyTriggerOnPageSelected) {
						listener.onPageSelected(arg0 - 1);
					}
					alreadyTriggerOnPageSelected = false;
				}

			}

		}

		@Override
		public void setOnPageChangeListener(OnPageChangeListener listener) {
			super.setOnPageChangeListener(listener == null ? null
					: new MyOnPageChangeListener(listener));
		}
		
		@Override
		public int getCurrentItem() {
			return super.getCurrentItem() - 1;
		}

		@Override
		public void setCurrentItem(int item) {
			setCurrentItem(item, true);
		}

		@Override
		public void setCurrentItem(int item, boolean smoothScroll) {
			if (adapter == null) {
				return;
			}
			if (adapter.getCount() <= 1) {
				super.setCurrentItem(0, smoothScroll);
			}else {
				super.setCurrentItem(item + 1, smoothScroll);
			}
		}
		
		@Override
		public void setOffscreenPageLimit(int limit) {
			if (dataAdapter != null && dataAdapter.getCount() < limit) {
				limit = dataAdapter.getCount();
			}
			super.setOffscreenPageLimit(limit);
		}
}
